/*
 *
 * $Id: wrap_pykadm5.c 16 2008-01-22 08:14:45Z Iosif69 $
 *
 */
#include <Python.h>
#include <stdlib.h>
#include "kadmin.h"
#include "pykadm5.h"

static PyObject *Kadm5Error;

static void cb_destroy(void *);

static int debug_mode = 0;

// Callback function to destroy kadm5 handle
static void cb_destroy(handle)
	void * handle;
{
    if(debug_mode) printf("***** handle destroyed **** \n");
    kadm5_destroy(handle);
}

static PyObject *
wrap_init_with_password(PyObject *self, PyObject *args) {

	char *admin_principal, *password, *realm, *admin_server;
    void * handle = NULL;

    kadm5_config_params params; 
    kadm5_ret_t rc;
    krb5_context context; 
    char ** db_args = NULL;    

    if ( !PyArg_ParseTuple(args, "sszz", 
			   &admin_principal, &password, &realm, &admin_server) )
    	return NULL;
    /*
     * kadmin part 
     */
    rc = krb5_init_context(&context);                                                                                                                       
    if(rc){
		PyErr_SetString(Kadm5Error, "init_context: init_with_password");
    	return NULL;
    }
    
    memset((char *) &params, 0, sizeof(params));
    if(realm){
    	params.realm = realm;                                                                                                                                
    	params.mask |= KADM5_CONFIG_REALM;
    }
    if(admin_server){
    	params.admin_server = admin_server;                                                                                                                  
    	params.mask |= KADM5_CONFIG_ADMIN_SERVER;                                                                                                            
    }                                     
    
    rc = kadm5_init_with_password(
    		admin_principal,                                                                                                              
            password,                                                                                  
            KADM5_ADMIN_SERVICE,                                                                       
            &params,                                                                                   
            KADM5_STRUCT_VERSION,                                                                      
            KADM5_API_VERSION_2,                                                                       
	    db_args,
            &handle);
    if(rc) {
    	set_error_msg(context,rc,":init_with_password");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }
    if(handle == NULL){
		PyErr_SetString(Kadm5Error, "General error: null handle");
    	krb5_free_context(context);                                                                                                                          
		return NULL;
    }
    krb5_free_context(context);  
    /*
     * Build PyCObject from handle
     */
    return Py_BuildValue("N", PyCObject_FromVoidPtr(handle, cb_destroy));
}

static PyObject *
wrap_init_with_keytab(PyObject *self, PyObject *args) {
	char *admin_principal, *keytab, *realm, *admin_server;
    void * handle = NULL;
    
    kadm5_config_params params; 
    kadm5_ret_t rc;
    krb5_context context; 
    char ** db_args = NULL;    

    if ( !PyArg_ParseTuple(args, "szzz", 
			   &admin_principal, &keytab, &realm, &admin_server) )
    	return NULL;
    rc = krb5_init_context(&context);                                                                                                                       
    if(rc){
		PyErr_SetString(Kadm5Error, "init_context: init_with_keytab");
    	return NULL;
    }
    
    memset((char *) &params, 0, sizeof(params));
    if(realm){
    	params.realm = realm;                                                                                                                                
    	params.mask |= KADM5_CONFIG_REALM;
    }
    if(admin_server){
    	params.admin_server = admin_server;                                                                                                                  
    	params.mask |= KADM5_CONFIG_ADMIN_SERVER;                                                                                                            
    }                                     
    
    rc = kadm5_init_with_skey(
    		admin_principal,                                                                                                              
            keytab,                                                                                  
            KADM5_ADMIN_SERVICE,                                                                       
            &params,                                                                                   
            KADM5_STRUCT_VERSION,                                                                      
            KADM5_API_VERSION_2,                                                                       
	    db_args,
            &handle);

    if(rc) {
    	set_error_msg(context,rc,":init_with_keytab");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }
    if(handle == NULL){
		PyErr_SetString(Kadm5Error, "General error: null handle");
    	krb5_free_context(context);                                                                                                                          
		return NULL;
    }
    krb5_free_context(context);  
    /*
     * Build PyCObject from handle
     */
    return Py_BuildValue("N", PyCObject_FromVoidPtr(handle, cb_destroy));
}

static PyObject *
wrap_change_password(PyObject *self, PyObject *args) {
	char *target_principal, *new_password;
    void * PyCHandle = NULL;
    krb5_principal princ;                                                                                                                                
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    
	if ( !PyArg_ParseTuple(args, "Oss", &PyCHandle, &target_principal, &new_password) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
	memset(&princ, 0, sizeof(princ));                                                                                                                    
	rc = krb5_init_context(&context);                                                                                                                    
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: change_password");
	   	return NULL;
	}                                                                                                                                                    
    rc = krb5_parse_name(context, target_principal, &princ);                                                                                                     
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":change_password");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }                 
    rc = kadm5_chpass_principal(PyCObject_AsVoidPtr(PyCHandle), princ, new_password);                                                                                                    
    if (rc) {                                                                                                                                            
      	set_error_msg(context,rc,":change_password");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    krb5_free_context(context); 
	return Py_BuildValue("i", 1);
}

static PyObject *
wrap_flush(PyObject *self, PyObject *args) {
    void * PyCHandle = NULL;
    kadm5_ret_t rc;
    
	if ( !PyArg_ParseTuple(args, "O", &PyCHandle) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
    rc = kadm5_flush( PyCObject_AsVoidPtr(PyCHandle) );                                                                                                                               
	if(rc){
		PyErr_SetString(Kadm5Error, "Handle flush error :flush");
		return NULL;
	}
	return Py_BuildValue("i", 1);
}

static PyObject *
wrap_delete_principal(PyObject *self, PyObject *args) {
	char *target_principal;
    void * PyCHandle = NULL;
    krb5_principal princ;                                                                                                                                
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    
	if ( !PyArg_ParseTuple(args, "Os", &PyCHandle, &target_principal) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
	memset(&princ, 0, sizeof(princ));                                                                                                                    
	rc = krb5_init_context(&context);                                                                                                                    
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: delete_principal");
	   	return NULL;
	}                                                                                                                                                    
    rc = krb5_parse_name(context, target_principal, &princ);                                                                                                     
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":delete_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }                 
    rc = kadm5_delete_principal(PyCObject_AsVoidPtr(PyCHandle), princ);                                                                                                    
    if (rc) {                                                                                                                                            
      	set_error_msg(context,rc,":delete_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    krb5_free_context(context); 
	return Py_BuildValue("i", 1);
}

static PyObject *
wrap_get_principals(PyObject *self, PyObject *args) {

    void * PyCHandle = NULL;
    char **princs;                                                                                                                                
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    int count,i;
    PyObject * ret_list; 
    
	if ( !PyArg_ParseTuple(args, "O", &PyCHandle) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
	rc = krb5_init_context(&context);                                                                                                                    
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: get_principals");
	   	return NULL;
	}                                                                                                                                                    
	
    rc = kadm5_get_principals(PyCObject_AsVoidPtr(PyCHandle), NULL, &princs, &count);                                                                                            
    if (rc) {                                                                                                                                            
      	set_error_msg(context,rc,":get_principals");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    krb5_free_context(context); 
    ret_list = PyList_New(0);
    for(i=0; i<count; i++) {              
    	PyList_Append(ret_list, PyString_FromString(princs[i]));                                                                                         
    	free(princs[i]);
    }                                                                                                                                                    
	return Py_BuildValue("N", ret_list);
}

static PyObject *
wrap_get_policies(PyObject *self, PyObject *args) {

    void * PyCHandle = NULL;
    char **policies;                                                                                                                                
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    int count,i;
    PyObject * ret_list; 
    
	if ( !PyArg_ParseTuple(args, "O", &PyCHandle) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
	rc = krb5_init_context(&context);                                                                                                                    
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: get_policies");
	   	return NULL;
	}                                                                                                                                                    
	
    rc = kadm5_get_policies(PyCObject_AsVoidPtr(PyCHandle), NULL, &policies, &count);                                                                                            
    if (rc) {                                                                                                                                            
      	set_error_msg(context,rc,":get_policies");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    krb5_free_context(context); 
    ret_list = PyList_New(0);
    for(i=0; i<count; i++) {              
    	PyList_Append(ret_list, PyString_FromString(policies[i]));                                                                                         
    	free(policies[i]);
    }                                                                                                                                                    
	return Py_BuildValue("N", ret_list);
}

static PyObject *
wrap_rename_principal(PyObject *self, PyObject *args) {
    char *old_principal;
    char *new_principal;
    void * PyCHandle = NULL;
    krb5_principal old_princ;                                                                                                                                
    krb5_principal new_princ;                                                                                                                                
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    
	if ( !PyArg_ParseTuple(args, "Oss", &PyCHandle, &old_principal, &new_principal) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
	memset(&old_princ, 0, sizeof(new_princ));                                                                                                                    
	memset(&new_princ, 0, sizeof(new_princ));                                                                                                                    
	rc = krb5_init_context(&context);                                                                                                                    
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: rename_principal");
	   	return NULL;
	}                                                                                                                                                    
    rc = krb5_parse_name(context, old_principal, &old_princ);                                                                                                     
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":rename_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }                 
    rc = krb5_parse_name(context, new_principal, &new_princ);                                                                                                     
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":rename_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }                 
    
    rc = kadm5_rename_principal(PyCObject_AsVoidPtr(PyCHandle), old_princ, new_princ);
    if (rc) {                                                                                                                                            
      	set_error_msg(context,rc,":rename_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    krb5_free_context(context); 
    return Py_BuildValue("i", 1);
}

static PyObject *
wrap_get_principal(PyObject *self, PyObject *args) {
    char *principal;
    char *modified_by;
    void * PyCHandle = NULL;
    krb5_principal princ;                                                                                                                                
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    kadm5_principal_ent_rec ent;
    //PyObject *key, *val;
    PyObject * dict;

	if ( !PyArg_ParseTuple(args, "Os", &PyCHandle, &principal) ) 
		return NULL;
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}
	memset(&princ, 0, sizeof(princ));
	memset(&ent, 0, sizeof(ent));
	rc = krb5_init_context(&context);                                                                                                                    
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: get_principal");
	   	return NULL;
	}                                                                                                                                                    
    rc = krb5_parse_name(context, principal, &princ);                                                                                                     
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":get_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }                 
	
    rc = kadm5_get_principal(PyCObject_AsVoidPtr(PyCHandle), princ, &ent, KADM5_PRINCIPAL_NORMAL_MASK);
    if (rc) {                                                                                                                                            
      	set_error_msg(context,rc,":get_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    
    rc = krb5_unparse_name(context, ent.principal, &principal);                                                                                                                                              
    if (rc) {
      	set_error_msg(context,rc,":get_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    rc = krb5_unparse_name(context, ent.mod_name, &modified_by);                                                                                                                                              
    if (rc) {
      	set_error_msg(context,rc,":get_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
        return NULL;
    }
    
    dict = PyDict_New();
    
    PyDict_SetItemString(dict,"principal",       PyString_FromString(principal));
    PyDict_SetItemString(dict,"modified_by",     PyString_FromString(modified_by));
    PyDict_SetItemString(dict,"expired",         PyInt_FromLong(ent.princ_expire_time));
    PyDict_SetItemString(dict,"last_pw_change",  PyInt_FromLong(ent.last_pwd_change));
    PyDict_SetItemString(dict,"pw_expired",      PyInt_FromLong(ent.pw_expiration));    
    PyDict_SetItemString(dict,"max_life",        PyInt_FromLong(ent.max_life));    
    PyDict_SetItemString(dict,"max_renew_life",  PyInt_FromLong(ent.max_renewable_life));
    PyDict_SetItemString(dict,"kvno",            PyInt_FromLong(ent.kvno));    
    PyDict_SetItemString(dict,"mod_date",        PyInt_FromLong(ent.mod_date));    
    PyDict_SetItemString(dict,"last_success",    PyInt_FromLong(ent.last_success));    
    PyDict_SetItemString(dict,"last_failed",     PyInt_FromLong(ent.last_failed));    
    PyDict_SetItemString(dict,"fail_auth_count", PyInt_FromLong(ent.fail_auth_count));    
    PyDict_SetItemString(dict,"attributes",      PyInt_FromLong(ent.attributes));    
    if(ent.policy)
      PyDict_SetItemString(dict,"policy",        PyString_FromString(ent.policy));    
    else
      PyDict_SetItemString(dict,"policy",        PyString_FromString(""));    
    free(modified_by);
    free(principal);
    krb5_free_context(context);
    return Py_BuildValue("N", dict);
}

static PyObject *
wrap_create_principal(PyObject *self, PyObject *args) {
	int randkey = 0;
    void * PyCHandle = NULL;
    char * new_principal;
    char * new_password;
    PyObject * params = NULL;  
    PyObject * value;
    
    long mask = 0;
    krb5_context context;                                                                                                                                
    kadm5_ret_t rc;
    kadm5_principal_ent_rec princ;
    kadm5_policy_ent_rec defpol;
    
    char randbuf[256];
    
	//memset(&princ,0,sizeof(princ));
    
	if ( !PyArg_ParseTuple(args, "OszO!", &PyCHandle, &new_principal, &new_password,
		&PyDict_Type, &params) ) 
			return NULL;
	
	if( !PyCObject_Check( (PyObject *)PyCHandle) ){
		PyErr_SetString(Kadm5Error, "Argument is not a PyCObject");
		return NULL;
	}

	if( new_password == NULL) 
		randkey = 1;
	
	rc = krb5_init_context(&context);
	if( rc ) {                                                                                                                                           
		PyErr_SetString(Kadm5Error, "init_context: create_principal");
	   	return NULL;
	}                     
	
	mask |= KADM5_PRINCIPAL;
	rc = krb5_parse_name(context,new_principal,&princ.principal);
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":create_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }      
    /*
    Dictionary keys:
    	random 			- present or not
    	policy			- policy name
    */
    
    
    value = PyDict_GetItem(params, PyString_FromString("policy"));
    if(value){
    	//
    	// if no policy defined on server, assume no policy
    	//
    	rc = kadm5_get_policy(PyCObject_AsVoidPtr(PyCHandle),PyString_AsString(value),&defpol);
    	if( !rc ){
    		princ.policy = PyString_AsString(value);
    		mask |= KADM5_POLICY;
    		(void) kadm5_free_policy_ent(PyCObject_AsVoidPtr(PyCHandle),&defpol);
    	}
    }else {
    	//
    	// policy not specified, using "default" if defined on kdc
    	// 
    	rc = kadm5_get_policy(PyCObject_AsVoidPtr(PyCHandle),"default",&defpol);
    	if( !rc ){
    		princ.policy = "default";
    		mask |= KADM5_POLICY;
    		(void) kadm5_free_policy_ent(PyCObject_AsVoidPtr(PyCHandle),&defpol);
    	}
    }

    value = PyDict_GetItem(params, PyString_FromString("random"));
	if(value)
		randkey = 1;
	
	
	if(randkey){
		int i;
		for(i=0; i<256;i++)
			randbuf[i] = (i+1) % 256;
		new_password = randbuf;
	}
    
    
    rc = kadm5_create_principal(PyCObject_AsVoidPtr(PyCHandle),&princ,mask,new_password); 
    if( rc ) {                                                                                                                                           
    	set_error_msg(context,rc,":create_principal");
		PyErr_SetString(Kadm5Error, get_error_msg());
        krb5_free_context(context);                                                                                                                          
		return NULL;
    }      
    if(randkey) {
    	rc = kadm5_randkey_principal(PyCObject_AsVoidPtr(PyCHandle),princ.principal,NULL,NULL);
    	if(rc){
        	set_error_msg(context,rc,":create_principal");
    		PyErr_SetString(Kadm5Error, get_error_msg());
            krb5_free_context(context);                                                                                                                          
    		return NULL;
    	}
    }
    
    free(context);
    return Py_BuildValue("i", 1);
}


static PyObject *
wrap_modify_principal(PyObject *self, PyObject *args) {
	PyErr_SetString(PyExc_NotImplementedError, "function modify_principal");
	return NULL;
}

static struct PyMethodDef pykadm5_methods[] = {
    {"init_with_password", wrap_init_with_password, METH_VARARGS, "Init Kerberos 5 Admin with principal and password"},
    {"init_with_keytab"  , wrap_init_with_keytab  , METH_VARARGS, "Init Kerberos 5 Admin with service principal and keytab"},
    {"flush"             , wrap_flush             , METH_VARARGS, "Flush all changes to the Kerberos database"},
    {"change_password"   , wrap_change_password   , METH_VARARGS, "Change principal password"},
    {"create_principal"  , wrap_create_principal  , METH_VARARGS, "Create principal"},
    {"delete_principal"  , wrap_delete_principal  , METH_VARARGS, "Delete principal"},
    {"modify_principal"  , wrap_modify_principal  , METH_VARARGS, "Modify principal attributes"},
    {"get_principal"     , wrap_get_principal     , METH_VARARGS, "Get principal attributes"},
    {"get_principals"    , wrap_get_principals    , METH_VARARGS, "Get principals list"},
    {"get_policies"      , wrap_get_policies      , METH_VARARGS, "Get policies list"},
    {"rename_principal"  , wrap_rename_principal  , METH_VARARGS, "Rename principal"},
    {NULL, NULL, 0, NULL}
};

 
/*
 * Initialization
 */

PyMODINIT_FUNC
init_pykadm5() {

    PyObject *m;

    m = Py_InitModule("_pykadm5", pykadm5_methods);
    Kadm5Error = PyErr_NewException("pykadm5.Kadm5Error", 
				    PyExc_Exception, NULL);
    Py_INCREF(Kadm5Error);
    PyModule_AddObject(m, "Kadm5Error", Kadm5Error);
}
